package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"iot-base/common/common"
	"iot-base/common/fsm"
	"iot-base/common/netip"
	"iot-base/common/tunnel/client"
	"iot-base/common/tunnel/morrior"
	"net"
	"net/http"
	"strings"
	"time"
)

var mapKCP common.ThrdSafeMap

func init() {
	mapKCP = common.CreateSafeMap()
}

//EdgeDetail 边缘节点信息
type edgeInfo struct {
	ID       string
	LastTime string
	Addrs    []string
	SvrAddr  string
}

func getEdgeByServerAddr(svrAddr string) []edgeInfo {
	//fmt.Println("getEdgeByServerAddr-------------")
	//vars := req.URL.Query()
	//addrs, ok := vars["svrAddr"]
	//w.Header().Set("Content-Type", "application-json")
	result := client.GetEdgeList(svrAddr)
	var edges []edgeInfo
	json.Unmarshal(result, &edges)
	for i := range edges {
		edges[i].Addrs, _ = getAddrList(edges[i].ID)
	}
	return edges

}
func getEdgeList(w http.ResponseWriter, req *http.Request) {
	fmt.Println("get-------------")
	vars := req.URL.Query()
	addrs, ok := vars["svrAddr"]
	fmt.Println("getEdgeList----------", addrs, ok)
	w.Header().Set("Content-Type", "application-json")
	if ok {

		if len(addrs) > 0 {
			addr := addrs[0]
			fmt.Println("getEdgeList--2--------", addr)
			edges := getEdgeByServerAddr(addr)
			bytes, _ := json.Marshal(&edges)
			if bytes != nil {
				w.Write([]byte(`{"result":`))
				w.Write(bytes)
				w.Write([]byte("}"))
				return
			}
			//w.Write(result)
		}
	} else {
		edgeIDs := mapKCP.GetKeys()
		if edgeIDs != nil {
			edges := make([]edgeInfo, 0, len(edgeIDs))
			for _, edgeID := range edgeIDs {
				edgeIDs, svrAddr := getAddrList(edgeID)
				edges = append(edges, edgeInfo{
					ID:      edgeID,
					Addrs:   edgeIDs,
					SvrAddr: svrAddr,
				})
			}
			bytes, _ := json.Marshal(&edges)
			if bytes != nil {
				w.Write(bytes)
				return
			}

		}

	}

	w.Write([]byte(`{"result":[]}`))

}

//TunnelOption 打洞参数
type TunnelOption struct {
	//edgeId,localPort
	SvrAddr   string      `json:"svrAddr"`
	EdgeId    string      `json:"edgeId"`
	LocalPort json.Number `json:"localPort"`
}

func getLocalAddr(pFsm *fsm.FSMInstance) string {
	addr, _ := pFsm.GetContext().GetString("localAddr")
	return addr
}
func doWakeup(tunnel TunnelOption, w http.ResponseWriter) (bool, string) {
	edgeId := tunnel.EdgeId
	fmt.Println("&&&&&&&&&&&&dowakeup*****get*****", edgeId)
	iConnection, _ := mapKCP.Get(edgeId)
	if iConnection == nil {
		port, _ := tunnel.LocalPort.Int64()
		pInstance, err := client.BeginClientTunnel(tunnel.SvrAddr, int(port), edgeId)
		if pInstance != nil && err == nil {

			fmt.Println("&&&&&&&&&&&&dowakeup****set******", edgeId)
			mapKCP.Set(edgeId, pInstance)
			laddr := getLocalAddr(pInstance)
			if w != nil {

				w.Write([]byte(`{"result":{"state":"begin","localPort":"` + laddr + `"}}`))
			}
			fmt.Println("&&&&&&&&&&&&&&&&&&&&1")
			return true, "begin"
		}
		if err != nil {
			if w != nil {
				w.Write([]byte(`{"err":"` + err.Error() + `"}`))
			}

			//return false
		} else {

			if w != nil {
				w.Write([]byte(`{"err":"fsm create error"}`))
			}
			//return
		}
		return false, "error"
	}
	pInstance, _ := iConnection.(*fsm.FSMInstance)
	var laddr string
	if pInstance != nil {
		laddr = getLocalAddr(pInstance)

		cfsm, _ := pInstance.GetContext().Get("connectionFSM")
		var pfsm *fsm.FSMInstance
		if cfsm != nil {
			pfsm, _ = cfsm.(*fsm.FSMInstance)

		}
		if pInstance.IsEnd() {
			if pfsm != nil {
				if pfsm.IsEnd() {
					w.Write([]byte(`{"result":{"state":"kcp end"}}`))
					fmt.Println("&&&&&&&&&&&&&&&&&&&&end")
					mapKCP.Remove(edgeId)
					return false, "kcp end"
				}
				if w != nil {
					w.Write([]byte(`{"result":{"state":"kcp ready","localAddr":"` + laddr + `"}}`))
				}

				fmt.Println("&&&&&&&&&&&&&&&&&&&&ready")
				return true, "ready"
			}
			if w != nil {
				w.Write([]byte(`{"result":{"state":"kcp not launched"}}`))
			}

			mapKCP.Remove(edgeId)
			return false, "not launched"
		}
	}
	if w != nil {
		w.Write([]byte(`{"result":{"state":"begin","localAddr":"` + laddr + `"}}`))
	}
	fmt.Println("&&&&&&&&&&&&&&&&&&&&0000000000")
	return true, "begin"
}
func wakeupList(w http.ResponseWriter, req *http.Request) {
	switch req.Method {
	case "PUT":
		body, _ := ioutil.ReadAll(req.Body)
		var tunnel TunnelOption
		if err := json.Unmarshal(body, &tunnel); err == nil {
			doWakeup(tunnel, w)
		} else {
			w.Write([]byte(`{"err":"` + err.Error() + `"`))
		}
	case "GET":
		eID := req.URL.Query().Get("edgeID")
		result, _ := getAddrList(eID)
		w.Header().Set("Content-Type", "application/json")
		if result != nil {
			message, _ := json.Marshal(result)
			w.Write([]byte(`{"result":`))
			w.Write(message)
			w.Write([]byte("}"))

		} else {
			w.Write([]byte(`{"result":[]}`))
		}

	}

}

func begin(w http.ResponseWriter, req *http.Request) {

	body, _ := ioutil.ReadAll(req.Body)

	//    r.Body.Close()
	//sbody := string(body)
	//fmt.Println(body_str)
	var mirror morrior.MorriorInfo
	//var tunnel TunnelOption
	if err := json.Unmarshal(body, &mirror); err == nil {
		if mirror.EdgeID == "" {
			w.Write([]byte(`{"err":"节点id为空"`))
			return
		}
		fmt.Println("**********info", string(body), mirror)
		isSucc, addr := beginConnection(mirror)
		if isSucc {
			w.Write([]byte(fmt.Sprintf(`{"addr":"%s"}`, addr)))
			fmt.Println("*****************suc****")
		} else {
			w.Write([]byte(`{"result":{"state":"waiting"}}`))
			fmt.Println("*****************failed**waiting**")
		}

	} else {
		w.Write([]byte(`{"err":"` + err.Error() + `"`))
		fmt.Println("*****************failed**timeout**")
	}

}

const default_svr_addr = "42.192.199.243:39999"

func beginConnection(info morrior.MorriorInfo) (bool, string) {
	edges := getEdgeByServerAddr(default_svr_addr)

	for _, edge := range edges {
		eparts := strings.Split(edge.ID, ":::")
		meparts := strings.Split(info.EdgeID, ":::")
		if eparts[0] == meparts[0] {
			info.EdgeID = edge.ID
			break
		}
	}
	return beginConnectionImpl(info)

}

func beginConnectionImpl(info morrior.MorriorInfo) (bool, string) {
	pfsm := getConnectionFSM(info.EdgeID)
	if pfsm != nil {
		fmt.Println("!!!!!!!-----fsm-----found--------")
		pfsm.NotifyEventWithParam("mirror", info)
		waitTimes := 0
		for {

			_, con := morrior.HasListenner(info, pfsm)
			if con != nil {
				addr := con.Addr().String()
				fmt.Println("!!!!!!!-----------------ok---beginConnection------succ", addr)
				return true, addr
			} else {
				if waitTimes > 2 {
					break
				}
				time.Sleep(2 * time.Second)
				waitTimes++
			}
		}

		fmt.Println("!!!!!!!beginConnection---HasListenner---not found")
	} else {
		fmt.Println("%%%%%%%%%%%not found fsm%%%%%%%%%%%%%%")
		if info.SvrAddr == "" {
			info.SvrAddr = default_svr_addr
		}
		times := 0
		for {

			_, state := doWakeup(TunnelOption{
				SvrAddr: info.SvrAddr,
				EdgeId:  info.EdgeID,
			}, nil)
			if state == "ready" {
				fmt.Println("-----------------ok---------ready")
				break
			}
			times++
			time.Sleep(time.Duration(2) * time.Second)
			if times > 40 {
				return false, ""
			}

		}

		return beginConnection(info)

	}
	return false, ""

}

func getConnectionFSM(edgeId string) *fsm.FSMInstance {
	iConnection, _ := mapKCP.Get(edgeId)

	if iConnection != nil {
		pInstance, _ := iConnection.(*fsm.FSMInstance)

		if pInstance != nil {
			cfsm, _ := pInstance.GetContext().Get("connectionFSM")
			if cfsm == nil {
				return nil
			}
			pfsm, _ := cfsm.(*fsm.FSMInstance)
			return pfsm

		}
	}
	return nil
}

func getAddrList(edgeId string) ([]string, string) {
	pFSM := getConnectionFSM(edgeId)
	if pFSM != nil {
		pList := morrior.GetAddrList(pFSM)
		if pList != nil {
			result := make([]string, 0, pList.Length())
			pList.Range(func(index int, item interface{}) bool {
				addr := item.(string)
				result = append(result, addr)
				return false //如果要中断遍历，请返回true
			})
			svr, _ := pFSM.GetContext().GetString("serverAddr")
			return result, svr
		} else {
			fmt.Println("error----------plist is nil------")
		}
	} else {
		fmt.Println("error-----------no fsm found------")
	}
	return nil, ""
}
func sendData(mip string, port int, protocol []byte, ip string, ifName string) ([]map[string]interface{}, error) {

	if ip == "" {
		ip = "0.0.0.0"
	}
	//pAddr, _ := net.ResolveUDPAddr("udp4", ip+":0")

	raddr, _ := net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%d", mip, port))
	ifs, _ := net.Interfaces()
	//isOK := false
	for _, pif := range ifs {
		if pif.Name == ifName {
			gaddr, _ := net.ResolveUDPAddr("udp4", mip+":0")
			conn, _ := net.ListenMulticastUDP("udp4", &pif, gaddr)
			if conn != nil {
				defer conn.Close()
				wlen, werr := conn.WriteToUDP(protocol, raddr)
				fmt.Println("WriteToUDP----err-------", werr, raddr, wlen, gaddr)
				var msg [1024]byte
				result := make([]map[string]interface{}, 0)
				conn.SetReadDeadline(time.Now().Add(time.Second * 15))
				for {
					nLen, _, err := conn.ReadFromUDP(msg[:])
					if err == nil {
						var item map[string]interface{}
						json.Unmarshal(msg[:nLen], &item)
						result = append(result, item)
						fmt.Println("****************recv-----item-----", item, len(result))
					} else {
						fmt.Println("****************recv-----timeout-----", conn)
						break
					}
				}
				return result, nil
			}

		}
	}

	return nil, errors.New("connection created failed")

}
func scanEdges(w http.ResponseWriter, req *http.Request) {

	ip := req.URL.Query().Get("ip")
	ifName := req.URL.Query().Get("ifname")
	fmt.Println("****************scan----------", ip, ifName)
	result, err := sendData("224.0.0.118", 16665, []byte(`{"type":"ip"}`), ip, ifName)
	if err != nil {
		fmt.Println("****************recv-----error-----", err)
		w.Write([]byte(`{"err":"` + err.Error() + `"}`))
	} else {
		mresult := make(map[string]interface{})
		mresult["result"] = result
		bres, err := json.Marshal(mresult)
		if err != nil {
			fmt.Println("****************json error-----", err)
			w.Write([]byte(`{"err":"` + err.Error() + `"}`))
		} else {
			fmt.Println("************get result-----", bres, mresult)
			w.Write(bres)
		}
	}
}

func getInterfaces(w http.ResponseWriter, req *http.Request) {
	ips, err := netip.GetIPs()
	if err != nil {
		w.Write([]byte(`{"err":"` + err.Error() + `"}`))
	} else {
		w.Write([]byte(`{"result":`))
		rb, _ := json.Marshal(ips)
		if rb != nil {
			w.Write(rb)
		}
		w.Write([]byte("}"))
	}
}
